/**
 * ReportGear(2011)
 */
package com.reportgear.report.model;

import java.awt.Rectangle;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.reportgear.core.api.BaseObject;
import com.reportgear.report.model.auxiliary.ColumnPropList;
import com.reportgear.report.model.auxiliary.ColumnProperty;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.auxiliary.ColumnType;
import com.reportgear.report.model.auxiliary.RowPropList;
import com.reportgear.report.model.auxiliary.RowProperty;
import com.reportgear.report.model.auxiliary.RowType;
import com.reportgear.report.model.cell.Cell;
import com.reportgear.report.model.cell.Cell.CR;
import com.reportgear.report.parameter.model.Parameter;
import com.reportgear.report.setting.ReportSettings;
import com.reportgear.report.view.JWorksheet;
import com.reportgear.report.view.grid.selection.Selection;
import com.sot.core.util.NumberUtils;
import com.sot.core.util.StringUtils;

/**
 * 报表模型
 * 
 * @version 1.0 2011-3-11
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class ReportModel extends BaseObject {
	protected static final Logger logger = LoggerFactory.getLogger(ReportModel.class.getName());
	/**
	 * 默认列宽度
	 */
	protected static final int DEFAULT_COLUMN_WIDTH = 72;
	/**
	 * 默认行高度
	 */
	protected static final int DEFAULT_ROW_HEIGHT = 24;

	// 模型Id,对应数据库表中的主键Id
	private int id;
	// 模型名称
	private String name;
	// 版本，每一个继承类设置自己的版本
	private String version = "1.0";
	// 备注,每一个继承类设置自己的备注
	private String comment = "通用模型";

	// 单元格对象
	private CellGrid cellGrid;
	// 支持属性改变事件支持
	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
	// 列属性列表
	private ColumnPropList columnPropList;
	// 行属性列表
	private RowPropList rowPropList;
	// 报表配置
	private ReportSettings reportSettings;
	// 模型参数
	private List<Parameter> parameters;

	/**
	 * 构造函数
	 */
	public ReportModel() {
		this.columnPropList = new ColumnPropList(DEFAULT_COLUMN_WIDTH);
		this.rowPropList = new RowPropList(DEFAULT_ROW_HEIGHT);
		this.reportSettings = new ReportSettings();
		this.cellGrid = new CellGrid();
		this.parameters = new ArrayList<Parameter>();
	}

	/**
	 * 获取模型读写器
	 * 
	 * @return 模型读写器
	 */
	protected ReportModelReader getReportModelReader() {
		return new ReportModelReader();
	}

	/**
	 * 添加属性改变监听器
	 * 
	 * @param listener
	 *            监听器
	 */
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		this.propertyChangeSupport.addPropertyChangeListener(listener);
	}

	/**
	 * 移除属性改变监听器
	 * 
	 * @param listener
	 *            监听器
	 */
	public void removePropertyChangeListener(PropertyChangeListener listener) {
		this.propertyChangeSupport.removePropertyChangeListener(listener);
	}

	/**
	 * 触发属性改变事件
	 * 
	 * @param propertyName
	 *            属性名称
	 * @param oldValue
	 *            历史值
	 * @param newValue
	 *            新值
	 */
	private void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
		this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
	}

	/**
	 * 获取列属性列表
	 * 
	 * @return 列属性列表
	 */
	public ColumnPropList getColumnPropList() {
		return this.columnPropList;
	}

	/**
	 * 获取行属性列表
	 * 
	 * @return 行属性列表
	 */
	public RowPropList getRowPropList() {
		return this.rowPropList;
	}

	/**
	 * 获取Cell像素范围
	 * 
	 * @param worksheet
	 *            工作薄
	 * @param cell
	 *            单元格
	 * @return Cell像素范围
	 */
	public Rectangle getCellRectangle(JWorksheet worksheet, Cell cell) {
		return this.getCellRectangle(worksheet, cell.getColumn(), cell.getRow(), cell.getColumnSpan(), cell
				.getRowSpan());
	}

	/**
	 * 获取Cell像素范围
	 * 
	 * @param worksheet
	 *            工作薄
	 * @param selection
	 *            选择集
	 * @return Cell像素范围
	 */
	public Rectangle getCellRectangle(JWorksheet worksheet, Selection selection) {
		return this.getCellRectangle(worksheet, selection.getColumn(), selection.getRow(), selection.getColumSpan(),
				selection.getRowSpan());
	}

	/**
	 * 获取Cell像素范围
	 * 
	 * @param column
	 *            Cell所在列
	 * @param row
	 *            Cell所在行
	 * @param columnSpan
	 *            Cell列跨度
	 * @param rowSpan
	 *            Cell行跨度
	 * @return Cell像素范围
	 */
	public Rectangle getCellRectangle(JWorksheet worksheet, int column, int row, int columnSpan, int rowSpan) {
		int x = this.columnPropList.getRangeValue(worksheet.getHoriBeginValue(), column);
		int y = this.rowPropList.getRangeValue(worksheet.getVertBeginValue(), row);

		int w = 0;
		for (int i = 0; i < columnSpan; i++) {
			w += this.columnPropList.getValue(column + i);
		}

		int h = 0;
		for (int j = 0; j < rowSpan; j++) {
			h += this.rowPropList.getValue(row + j);
		}

		return new Rectangle(x, y, w, h);
	}

	/**
	 * 添加列
	 * 
	 * @param index
	 *            列索引
	 */
	public void addColumn(int index) {
		this.checkMergedCells(this.getColumn(index));

		this.columnPropList.add(index, new ColumnProperty(DEFAULT_COLUMN_WIDTH));
		this.cellGrid.addColumn(index);

		this.firePropertyChange("ColumnCount", null, this.getColumnCount());
	}

	/**
	 * 添加行
	 * 
	 * @param index
	 *            行索引
	 */
	public void addRow(int index) {
		this.checkMergedCells(this.getRow(index));

		this.rowPropList.add(index, new RowProperty(DEFAULT_ROW_HEIGHT));
		this.cellGrid.addRow(index);

		this.firePropertyChange("RowCount", null, this.getRowCount());
	}

	/**
	 * 移除列
	 * 
	 * @param index
	 *            列索引(0 基)
	 */
	public void removeColumn(int index) {
		this.checkMergedCells(this.getColumn(index));

		this.columnPropList.remove(index);
		this.cellGrid.deleteColumn(index);

		this.firePropertyChange("ColumnCount", null, this.getColumnCount());
	}

	/**
	 * 移除行
	 * 
	 * @param index
	 *            行索引(0 基)
	 */
	public void removeRow(int index) {
		this.checkMergedCells(this.getRow(index));
		this.rowPropList.remove(index);
		this.cellGrid.deleteRow(index);

		this.firePropertyChange("RowCount", null, this.getRowCount());
	}

	/**
	 * 检查合并单元格，如果单元格被合并，则拆分合并主格
	 * 
	 * @param cells
	 *            检查单元格集
	 */
	private void checkMergedCells(Iterator<Cell> cells) {
		while (cells.hasNext()) {
			Cell cell = cells.next();
			if (cell.isMerged()) {
				this.unmergeCell(this.getCell(cell.getMergedToCR()));
			}
		}
	}

	/**
	 * 获取指定单元格
	 * 
	 * @param column
	 *            行
	 * @param row
	 *            列
	 * @return 单元格
	 */
	public Cell getCell(int column, int row) {
		this.checkRow(row);
		this.checkColumn(column);

		return this.cellGrid.getCell(column, row);
	}

	/**
	 * 获取指定单元格
	 * 
	 * @param cr
	 *            合并目标
	 * @return 单元格
	 */
	public Cell getCell(CR cr) {
		return this.getCell(cr.getColumn(), cr.getRow());
	}

	/**
	 * 获取指定单元格
	 * 
	 * @param sel
	 *            区域
	 * @return 单元格
	 */
	public Cell getCell(Selection sel) {
		return this.getCell(sel.getColumn(), sel.getRow());
	}

	/**
	 * 检查行
	 * 
	 * @param row
	 *            行
	 */
	private void checkRow(int row) {
		int count = this.getRowCount();
		int size = row + 1;

		if (count >= size) {
			return;
		}

		for (int i = count; i < size; i++) {
			if (this.rowPropList.size() <= i) {
				this.rowPropList.add(i, new RowProperty(DEFAULT_ROW_HEIGHT));
			}
			this.cellGrid.addRow(i);
		}

		this.firePropertyChange("RowCount", null, this.getRowCount());
	}

	/**
	 * 检查列
	 * 
	 * @param column
	 *            列
	 */
	private void checkColumn(int column) {
		int count = this.getColumnCount();
		int size = column + 1;

		if (count >= size) {
			return;
		}

		for (int i = count; i < size; i++) {
			if (this.columnPropList.size() <= i) {
				this.columnPropList.add(i, new ColumnProperty(DEFAULT_COLUMN_WIDTH));
			}
			this.cellGrid.addColumn(i);
		}

		this.firePropertyChange("ColumnCount", null, this.getColumnCount());
	}

	/**
	 * 获取列单元格迭代器
	 * 
	 * @param index
	 *            列索引
	 * @return 列单元格迭代器
	 */
	public Iterator<Cell> getColumn(int index) {
		return this.cellGrid.getColumn(index);
	}

	/**
	 * 获取所有单元格迭代器
	 * 
	 * @return 单元格迭代器
	 */
	public Iterator<Cell> getCellIterator() {
		return this.cellGrid.cellIterator();
	}

	/**
	 * 获取包含指定对象类的Cell迭代器
	 * 
	 * @param clazz
	 *            指定对象类
	 * @return 迭代器
	 */
	public Iterator<Cell> getCellsIteratorByCellElementClass(Class<?> clazz) {
		return this.cellGrid.getCellsByElementClass(clazz);
	}

	/**
	 * 获取行单元格索引
	 * 
	 * @param index
	 *            行索引
	 * @return 行单元格索引
	 */
	public Iterator<Cell> getRow(int index) {
		return this.cellGrid.getRow(index);
	}

	/**
	 * 
	 * Cell交叉集
	 * 
	 * @param columnFrom
	 *            开始行 0 based
	 * @param rowFrom
	 *            开始列 0 based
	 * @param columnSpan
	 *            列跨度
	 * @param rowSpan
	 *            行跨度
	 * @return 迭代器
	 */
	public Iterator<Cell> intersect(int columnFrom, int rowFrom, int columnTo, int rowTo) {
		return this.cellGrid.intersect(columnFrom, rowFrom, columnTo, rowTo);
	}

	/**
	 * 获取列宽度
	 * 
	 * @param index
	 *            列索引
	 * @return 列宽度
	 */
	public int getColumnWidth(int index) {
		return this.columnPropList.getValue(index);
	}

	/**
	 * 设置列宽度
	 * 
	 * @param index
	 *            列索引
	 * @param width
	 *            列宽度
	 */
	public void setColumnWidth(int index, int width) {
		this.columnPropList.setValue(index, width);
	}

	/**
	 * 获取列类型
	 * 
	 * @param index
	 *            列索引
	 * @return 列类型
	 */
	public ColumnType getColumnType(int index) {
		return this.columnPropList.getType(index);
	}

	/**
	 * 设置列类型
	 * 
	 * @param index
	 *            列索引
	 * @param rowType
	 *            列类型
	 */
	public void setColumnType(int index, ColumnType columnType) {
		this.columnPropList.setType(index, columnType);
	}

	/**
	 * 获取行高度
	 * 
	 * @param index
	 *            行索引
	 * @return 行高度
	 */
	public int getRowHeight(int index) {
		return this.rowPropList.getValue(index);
	}

	/**
	 * 设置行高度
	 * 
	 * @param index
	 *            行索引
	 * @param height
	 *            行高度
	 */
	public void setRowHeight(int index, int height) {
		this.rowPropList.setValue(index, height);
	}

	/**
	 * 获取行类型
	 * 
	 * @param index
	 *            行索引
	 * @return 行类型
	 */
	public RowType getRowType(int index) {
		return this.rowPropList.getType(index);
	}

	/**
	 * 设置行类型
	 * 
	 * @param index
	 *            索引
	 * @param rowType
	 *            行类型
	 */
	public void setRowType(int index, RowType rowType) {
		this.rowPropList.setType(index, rowType);
	}

	/**
	 * 合并单元格
	 * 
	 * @param column
	 *            列
	 * 
	 * @param row
	 *            行
	 * @param columnSpan
	 *            列跨度
	 * @param rowSpan
	 *            行跨度
	 */
	public void mergeCell(int column, int row, int columnSpan, int rowSpan) {
		this.checkColumn(column + columnSpan - 1);
		this.checkRow(row + rowSpan - 1);

		Cell cell = this.cellGrid.getCell(column, row);
		Iterator<Cell> iterator = this.cellGrid.intersect(column, row, columnSpan, rowSpan);
		while (iterator.hasNext()) {
			Cell temp = iterator.next();
			if ((temp.getColumn() == column) && (temp.getRow() == row)) {
				temp.setColumnSpan(columnSpan);
				temp.setRowSpan(rowSpan);
				temp.setMerged(false);
			} else {
				temp.clear();
				temp.setColumnSpan(0);
				temp.setRowSpan(0);
				temp.mergeIntoCell(column, row);
				temp.setMerged(true);
			}
		}
	}

	/**
	 * 取消单元格合并
	 * 
	 * @param column
	 *            列
	 * @param row
	 *            行
	 * @param columnSpan
	 *            列跨度
	 * @param rowSpan
	 *            行跨度
	 */
	public void unmergeCell(int column, int row, int columnSpan, int rowSpan) {
		Iterator<Cell> iterator = this.cellGrid.intersect(column, row, columnSpan, rowSpan);

		while (iterator.hasNext()) {
			Cell temp = iterator.next();
			temp.setMerged(false);
			temp.setColumnSpan(1);
			temp.setRowSpan(1);
		}
	}

	/**
	 * 取消单元格合并
	 * 
	 * @param cell
	 *            单元格
	 */
	private void unmergeCell(Cell cell) {
		if (cell.getColumnSpan() > 1 || cell.getRowSpan() > 1) {
			this.unmergeCell(cell.getColumn(), cell.getRow(), cell.getColumnSpan(), cell.getRowSpan());
		}
	}

	/**
	 * 设置单元格的值
	 * 
	 * @param column
	 *            列
	 * @param row
	 *            行
	 * @param value
	 *            值
	 */
	public void setCellValue(int column, int row, Object value) {
		this.checkColumn(column);
		this.checkRow(row);

		this.cellGrid.setCellValue(column, row, value);
	}

	/**
	 * 增加对象到指定的矩形框中的所有单元格
	 * 
	 * @param value
	 *            要加入的单元格对象
	 * @param sel
	 *            选择范围
	 */
	public void setCellValue(Selection sel, Object value) {
		int column = sel.getColumn();
		int row = sel.getRow();

		for (int i = 0, lenX = sel.getColumSpan(); i < lenX; i++) {
			for (int j = 0, lenY = sel.getRowSpan(); j < lenY; j++) {
				this.setCellValue(column, row, value);
			}
		}
	}

	/**
	 * 获取单元格的浮点值
	 * 
	 * @param column
	 *            行
	 * @param row
	 *            列
	 * @return 单元格浮点值
	 */
	public double getCellDoubleValue(int column, int row) {
		Cell cell = this.getCell(column, row);

		if (cell != null) {
			Object element = cell.getValue();

			if (element != null) {
				return NumberUtils.convertToDouble(element, 0.0D);
			}
		}

		return 0.0D;
	}

	/**
	 * 获取单元格的值
	 * 
	 * @param cr
	 *            ColumnRow对象
	 * @return 单元格浮点值
	 */
	public double getCellDoubleValue(ColumnRow cr) {
		return this.getCellDoubleValue(cr.getColumn(), cr.getRow());
	}

	/**
	 * @return 列总数
	 */
	public int getColumnCount() {
		return this.cellGrid.getColumnCount();
	}

	/**
	 * @return 行总数
	 */
	public int getRowCount() {
		return this.cellGrid.getRowCount();
	}

	/**
	 * 报表设置
	 * 
	 * @param reportSettings
	 *            报表设置
	 */
	public void setReportSettings(ReportSettings reportSettings) {
		this.reportSettings = reportSettings;
	}

	/**
	 * 获取报表设置
	 * 
	 * @return 报表设置
	 */
	public ReportSettings getReportSettings() {
		return reportSettings;
	}

	/**
	 * 添加参数
	 * 
	 * @param parameter
	 *            参数对象
	 */
	public void addParameter(Parameter parameter) {
		if (logger.isDebugEnabled()) {
			logger.debug("注册参数:" + parameter);
		}
		this.parameters.add(parameter);
	}

	/**
	 * 获取模型参数列表
	 * 
	 * @return 参数列表
	 */
	public List<Parameter> getParameters() {
		return this.parameters;
	}

	/**
	 * 根据参数名称获取参数值
	 * 
	 * @param name
	 *            参数名称
	 * @return 参数值
	 */
	public Object getParameter(String name) {
		if (!StringUtils.isEmpty(name)) {
			for (Parameter para : this.parameters) {
				if (name.equals(para.getName())) {
					return para.getObject();
				}
			}
		}

		return "";
	}

	/**
	 * 
	 * @return 获取固定列首
	 */
	public List<Integer> getFixedHeaderColumns() {
		return getColumnsByType(ColumnType.HEADER);
	}

	/**
	 * 
	 * @return 获取固定列尾
	 */
	public List<Integer> getFixedFooterColumns() {
		return getColumnsByType(ColumnType.FOOTER);
	}

	/**
	 * 获取指定类型的列
	 * 
	 * @param type
	 *            指定类型
	 * @return 列索引列表
	 */
	private List<Integer> getColumnsByType(ColumnType type) {
		List<Integer> result = new ArrayList<Integer>();

		for (int i = 0, size = this.getColumnCount(); i < size; i++) {
			ColumnProperty p = this.columnPropList.get(i);

			if (p.getType() == type) {
				result.add(i);
			}
		}

		return result;
	}

	/**
	 * 是否固定列
	 * 
	 * @param column
	 *            指定列
	 * @return 是否固定列
	 */
	public boolean isFixedColumn(int column) {
		ColumnProperty p = this.columnPropList.get(column);

		return p.getType() == ColumnType.HEADER || p.getType() == ColumnType.FOOTER;
	}

	/**
	 * 
	 * @return 获取固定行首
	 */
	public List<Integer> getFixedHeaderRows() {
		return getFixedRowsByType(RowType.HEADER);
	}

	/**
	 * 
	 * @return 获取固定行尾部
	 */
	public List<Integer> getFixedFooterRows() {
		return getFixedRowsByType(RowType.FOOTER);
	}

	/**
	 * 获取指定类型的行
	 * 
	 * @param type
	 *            指定类型
	 * @return 行索引列表
	 */
	private List<Integer> getFixedRowsByType(RowType type) {
		List<Integer> result = new ArrayList<Integer>();

		for (int i = 0, size = this.getRowCount(); i < size; i++) {
			RowProperty p = this.rowPropList.get(i);

			if (p.getType() == type) {
				result.add(i);
			}
		}

		return result;
	}

	/**
	 * 是否固定行
	 * 
	 * @param row
	 *            指定行
	 * @return 是否固定行
	 */
	public boolean isFixedRow(int row) {
		RowProperty p = this.rowPropList.get(row);

		return p.getType() == RowType.HEADER || p.getType() == RowType.FOOTER;
	}

	/**
	 * 报表模型克隆
	 * 
	 * @return 报表模型
	 */
	@Override
	public ReportModel clone() {
		ReportModel model = (ReportModel) super.clone();
		model.columnPropList = this.columnPropList.clone();
		model.rowPropList = this.rowPropList.clone();
		model.cellGrid = this.cellGrid.clone();

		return model;
	}

	/**
	 * 转化成XML字符串
	 * 
	 * @return XML格式字符串
	 */
	public String toXmlString() {
		return this.getReportModelReader().writeToXml(this);
	}

	/**
	 * 从XML读取模型属性
	 * 
	 * @param xmlString
	 *            XML格式字符串
	 */
	public void fromXmlString(String xmlString) {
		this.getReportModelReader().readFromXml(xmlString, this);
	}

	/**
	 * 获取Id
	 * 
	 * @return Id值
	 */
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	/**
	 * 设置版本
	 * 
	 * @param version
	 *            版本
	 */
	public void setVersion(String version) {
		this.version = version;
	}

	/**
	 * 获取版本
	 * 
	 * @return 版本
	 */
	public String getVersion() {
		return version;
	}

	/**
	 * 设置备注
	 * 
	 * @param comment
	 *            备注
	 */
	public void setComment(String comment) {
		this.comment = comment;
	}

	/**
	 * 获取备注
	 * 
	 * @return 备注
	 */
	public String getComment() {
		return comment;
	}

	/**
	 * 获取模型名称
	 * 
	 * @return 模型名称
	 */
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 复制行属性
	 * 
	 * @param dst
	 *            目标行索引
	 * @param src
	 *            源行索引
	 */
	public void copyRowProperties(int dst, int src) {
		// 复制高度
		this.setRowHeight(dst, this.getRowHeight(src));
		// 复制行属性
		this.setRowType(dst, this.getRowType(src));
		// 复制样式
		for (int i = 0, size = this.getColumnCount(); i < size; i++) {
			this.getCell(i, dst).setStyle(this.getCell(i, src).getStyle());
		}
	}
}
